package model

import (
	"time"

	"sync"

	"github.com/xlvector/dlog"
)

type IUPool struct {
	Wait      int64
	Threshold uint

	FirstInsertAt int64
	LastInsertAt  int64
	Size          uint
	Lock          *sync.Mutex

	Content []interface{}
}

type UniIUPool struct {
	Wait      int64
	Threshold uint

	FirstInsertAt int64
	LastInsertAt  int64
	Size          uint
	Lock          *sync.Mutex

	Content map[string]interface{}
}

func NewUniqIUPool(w int64, n uint) *UniIUPool {
	m := make(map[string]interface{})

	ret := &UniIUPool{Wait: w, Threshold: n, Size: 0,
		FirstInsertAt: 0, Content: m,
		Lock: new(sync.Mutex)}

	return ret
}

func NewIUPool(w int64, n uint) *IUPool {
	m := make([]interface{}, 0, n+10)

	ret := &IUPool{Wait: w, Threshold: n, Size: 0,
		FirstInsertAt: 0, Content: m,
		Lock: new(sync.Mutex)}

	return ret
}

func (p *IUPool) IsDone() bool {
	return time.Now().Unix()-p.FirstInsertAt > p.Wait || p.Size >= p.Threshold
}

//bool:是否达到WaitAt或者Threshold条件
func (p *IUPool) Insert(item interface{}) (bool, error) {
	func() {
		p.Lock.Lock()
		defer p.Lock.Unlock()

		if p.FirstInsertAt == 0 {
			p.FirstInsertAt = time.Now().Unix()
		}
		p.Content = append(p.Content, item)
		p.Size += 1
		p.LastInsertAt = time.Now().Unix()

	}()
	if p.Size >= p.Threshold || p.LastInsertAt-p.FirstInsertAt > p.Wait {
		return true, nil
	}

	return false, nil
}

func (p *IUPool) InsertMany(items []interface{}) (bool, error) {
	func() {
		p.Lock.Lock()
		defer p.Lock.Unlock()
		if p.FirstInsertAt == 0 {
			p.FirstInsertAt = time.Now().Unix()
		}
		for _, v := range items {
			p.Content = append(p.Content, v)
			p.Size += 1
		}
		p.LastInsertAt = time.Now().Unix()
	}()

	if p.Size >= p.Threshold || p.LastInsertAt-p.FirstInsertAt > p.Wait {
		return true, nil
	}

	return false, nil
}

func (p *IUPool) GetAll() []interface{} {
	ret := make([]interface{}, 0, len(p.Content))
	p.Lock.Lock()
	defer p.Lock.Unlock()

	for _, v := range p.Content {
		ret = append(ret, v)
	}
	p.Content = p.Content[0:0]
	p.Size = 0
	p.FirstInsertAt = 0

	return ret
}

func (p *UniIUPool) IsDone() bool {
	return time.Now().Unix()-p.FirstInsertAt > p.Wait || p.Size >= p.Threshold
}

//bool:是否达到WaitAt或者Threshold条件
func (p *UniIUPool) Insert(key string, item interface{}) (bool, error) {
	func() {
		p.Lock.Lock()
		defer p.Lock.Unlock()
		if p.FirstInsertAt == 0 {
			p.FirstInsertAt = time.Now().Unix()
		}
		_, ok := p.Content[key]
		if ok {
			dlog.Warn("duplicated key! %#v", key, item)
			return
		}
		p.Content[key] = item

		p.Size += 1
		p.LastInsertAt = time.Now().Unix()
	}()

	if p.Size >= p.Threshold || p.LastInsertAt-p.FirstInsertAt > p.Wait {
		return true, nil
	}

	return false, nil
}

func (p *UniIUPool) InsertMany(keys []string, items []interface{}) (bool, error) {
	func() {
		p.Lock.Lock()
		defer p.Lock.Unlock()
		if p.FirstInsertAt == 0 {
			p.FirstInsertAt = time.Now().Unix()
		}

		for i, v := range items {
			_, ok := p.Content[keys[i]]
			if ok {
				dlog.Warn("dumplicated key", keys[i])
				continue
			}
			p.Content[keys[i]] = v
			p.Size += 1
		}
		p.LastInsertAt = time.Now().Unix()
	}()

	if p.Size >= p.Threshold || p.LastInsertAt-p.FirstInsertAt > p.Wait {
		return true, nil
	}

	return false, nil
}

func (p *UniIUPool) GetAll() []interface{} {
	ret := make([]interface{}, 0, len(p.Content))
	p.Lock.Lock()
	defer p.Lock.Unlock()
	for k, v := range p.Content {
		ret = append(ret, v)
		delete(p.Content, k)
		p.Size -= 1
	}
	p.FirstInsertAt = 0

	return ret
}
